﻿using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Forms;
using OSGeo.GDAL;
using OSGeo.OGR;
using OSGeo.OSR;
using Driver = OSGeo.OGR.Driver;

namespace shpTools
{
    public class GdalTools
    {
        #region 中文乱码处理
        [DllImport("gdal204.dll", EntryPoint = "OGR_F_GetFieldAsString", CallingConvention = CallingConvention.Cdecl)]
        public extern static System.IntPtr OGR_F_GetFieldAsString(HandleRef handle, int i);

        [DllImport("gdal204.dll", EntryPoint = "CPLStrnlen", CallingConvention = CallingConvention.Cdecl)]
        public extern static uint CPLStrnlen(IntPtr handle, uint nMaxLen);

        [DllImport("gdal204.dll", EntryPoint = "OGR_Fld_GetNameRef", CallingConvention = CallingConvention.Cdecl)]
        public extern static System.IntPtr OGR_Fld_GetNameRef(HandleRef handle);

        /// <summary>
        /// 读取时调用
        /// </summary>
        /// <param name="fe">当前要素</param>
        /// <param name="index">索引</param>
        /// <returns></returns>
        public static String GetFieldValueString(Feature fe, int index, Encoding encoding)
        {
            IntPtr pNativeData = OGR_F_GetFieldAsString(OSGeo.OGR.Feature.getCPtr(fe), index);
            if (pNativeData == IntPtr.Zero)
                return null;
            if (encoding == Encoding.UTF8)
            {
                int len = (int)CPLStrnlen(pNativeData, uint.MaxValue);
                byte[] buf = new byte[len];
                Marshal.Copy(pNativeData, buf, 0, len);
                return System.Text.Encoding.UTF8.GetString(buf);
            }

            string s = Marshal.PtrToStringAnsi(pNativeData);
            return s;
        }

        [DllImport("gdal204.dll", EntryPoint = "OGR_F_SetFieldString", CallingConvention = CallingConvention.Cdecl)]
        public extern static void OGR_F_SetFieldString(HandleRef handle, int index, string val);
        /// 写入时调用
        /// <param name="fe"></param>
        /// <param name="index">索引</param>
        /// <param name="val">值</param>
        public static void SetFieldValueString(Feature fe, int index, string val)
        {

            //说白了封装C#版本的时候做了一层UTF8的转换
            OGR_F_SetFieldString(OSGeo.OGR.Feature.getCPtr(fe), index, val);
        }

        public static string GetFieldName(FieldDefn fieldDefn, Encoding encoding)
        {
            // 转为unicode二进制
            if (encoding == Encoding.UTF8)
            {
                IntPtr intPtr = OGR_Fld_GetNameRef(OSGeo.OGR.FieldDefn.getCPtr(fieldDefn));
                int len = (int)CPLStrnlen(intPtr, uint.MaxValue);
                byte[] buf = new byte[len];
                Marshal.Copy(intPtr, buf, 0, len);
                return System.Text.Encoding.UTF8.GetString(buf);
            } else
                return fieldDefn.GetName();
        }
        #endregion

        string wkt = "GEOGCS[\"China Geodetic Coordinate System 2000\",DATUM[\"China_2000\",SPHEROID[\"CGCS2000\",6378137,298.257222101,AUTHORITY[\"EPSG\",\"1024\"]],AUTHORITY[\"EPSG\",\"1043\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4490\"]]";//投影坐标系的WKT


        public static void InitGDAL()
        {
            shp2postgres.GdalConfiguration.ConfigureGdal();
        }

        public Encoding Encoding { get; set; }

        /// <summary>
        /// 测试写入shp文件的代码
        /// </summary>
        /// <param name="filePath"></param>
        /// <returns></returns>
        public Boolean WriteShp(String filePath)
        {

            string strDriver = "ESRI Shapefile";
            Driver oDriver = Ogr.GetDriverByName(strDriver);

            DataSource ds1 = oDriver.CreateDataSource(filePath, null);

            string wkt = "GEOGCS[\"China Geodetic Coordinate System 2000\",DATUM[\"China_2000\",SPHEROID[\"CGCS2000\",6378137,298.257222101,AUTHORITY[\"EPSG\",\"1024\"]],AUTHORITY[\"EPSG\",\"1043\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4490\"]]";//投影坐标系的WKT
            OSGeo.OSR.SpatialReference sr = new OSGeo.OSR.SpatialReference(wkt);
            Layer olayer1 = ds1.CreateLayer("PointLayer", sr, wkbGeometryType.wkbPoint, null);

            //接下来创建属性表字段
            // 先创建一个叫FieldID的整型属性
            FieldDefn oFieldID = new FieldDefn("FieldID", FieldType.OFTInteger);
            olayer1.CreateField(oFieldID, 1);

            FieldDefn fieldName = new FieldDefn("fname", FieldType.OFTString);
            fieldName.SetWidth(1000);
            olayer1.CreateField(fieldName, 1);

            FieldDefn fieldX = new FieldDefn("x", FieldType.OFTReal);
            fieldX.SetPrecision(8);
            fieldX.SetWidth(10);
            olayer1.CreateField(fieldX, 1);


            FeatureDefn featureDefn = olayer1.GetLayerDefn();
            Feature feature = new Feature(featureDefn);
            feature.SetField("FieldID", 1001);
            feature.SetField(1, "123456789012345678901234567890");
            feature.SetField(2, 120.345);
            Geometry geometry = new Geometry(wkbGeometryType.wkbPoint);
            geometry.AddPoint(120.345, 30.03, 10);
            feature.SetGeometry(geometry);

            olayer1.CreateFeature(feature);

            Feature feature2 = new Feature(featureDefn);
            feature2.SetField(0, 1002);
            //feature2.SetField(1, "name name 名 称 名 称   什 么 ！!d ..f");
            SetFieldValueString(feature2, 1, "name name 名 称 名 称   什 么 ！!d ..f");
            feature2.SetField(2, 121.8923);
            String geoWkt = "POINT(121.245 30.2345)";
            Geometry geom2 = Geometry.CreateFromWkt(geoWkt);
            //Geometry geom3 = Ogr.CreateGeometryFromWkt(ref geoWkt, olayer1.GetSpatialRef());
            feature2.SetGeometry(geom2);
            olayer1.CreateFeature(feature2);

            feature.Dispose();
            feature2.Dispose();

            olayer1.Dispose();
            ds1.Dispose();

            return true;
        }

        /// <summary>
        /// 读取shp格式的文件
        /// </summary>
        /// <param name="filePath"></param>
        /// <returns> 返回坐标系信息，字段信息和空间数据集合
        /// </returns>
        public (SpatialReference siref, FeatureDefn fields, List<Dictionary<String, Object>> dataList, List<Geometry> geoList) readShp(String filePath)
        {
            DataSource dataSource = Ogr.Open(filePath, 0); //0表示只读，1表示可修改
            if (null == dataSource) { MessageBox.Show("打开文件[{0}]失败！", filePath); return (null, null, null, null); }
            Layer layer = dataSource.GetLayerByIndex(0);
            if (null == layer)
            {
                MessageBox.Show("找不到图层");
                return (null, null, null, null);
            }
            SpatialReference siref = layer.GetSpatialRef();
            
            FeatureDefn featureDefn = layer.GetLayerDefn();
            int count = featureDefn.GetFieldCount();

            wkbGeometryType geoType = featureDefn.GetGeomType();
            List<Dictionary<String, Object>> dataList = new List<Dictionary<String, Object>>();
            List<Geometry> geomList = new List<Geometry>();
            Feature f = null;
            while ((f = layer.GetNextFeature()) != null)
            {
                Dictionary<String, Object> data = new Dictionary<String, Object>();
                for (int i = 0; i < count; i++)
                {
                    FieldDefn fieldDefn = f.GetFieldDefnRef(i);
                    data.Add(GetFieldName(fieldDefn, Encoding), GetFeatureFieldValueByIndex(f, fieldDefn, i));
                }

                //switch (geoType)
                //{
                //    case wkbGeometryType.wkbPoint:
                //        break;
                //    case wkbGeometryType.wkbLineString:
                //        break;
                //    case wkbGeometryType.wkbPolygon:
                //        break;
                //    case wkbGeometryType.wkbMultiPoint:
                //        break;
                //    case wkbGeometryType.wkbMultiLineString:
                //        break;
                //    case wkbGeometryType.wkbMultiPolygon:
                //        break;
                //    default:
                //        break;
                //}
                dataList.Add(data);
                geomList.Add(f.GetGeometryRef());
            }
            
            return (siref, featureDefn, dataList, geomList);
        }

        private Object GetFeatureFieldValue(Feature f, FieldDefn field)
        {
            String fn = field.GetName();
            Object r;
            switch (field.GetFieldType())
            {
                case FieldType.OFTInteger:
                    r = f.GetFieldAsInteger(fn);
                    break;
                case FieldType.OFTInteger64:
                    r = f.GetFieldAsInteger64(fn);
                    break;
                case FieldType.OFTString:
                    r = GetFieldValueString(f, f.GetFieldIndex(fn), Encoding);
                    break ;
                case FieldType.OFTReal:
                    r = f.GetFieldAsDouble(fn);
                    break;
                case FieldType.OFTWideString:
                    r = GetFieldValueString(f, f.GetFieldIndex(fn), Encoding);
                    break;
                case FieldType.OFTBinary:
                    r = f.GetFieldAsString(fn);
                    break;
                case FieldType.OFTDate:
                    int year, month, day, h, m, fg;
                    float s; 
                    f.GetFieldAsDateTime(fn, out year, out month, out day, out h, out m, out s, out fg);
                    r = new DateTime(year, month, day, h, m, fg);
                    break;
                case FieldType.OFTDateTime:
                    f.GetFieldAsDateTime(fn, out year, out month, out day, out h, out m, out s, out fg);
                    r = new DateTime(year, month, day, h, m, fg);
                    break;
                case FieldType.OFTTime:
                    f.GetFieldAsDateTime(fn, out year, out month, out day, out h, out m, out s, out fg);
                    r = new DateTime(year, month, day, h, m, fg);
                    break;
                default:
                    r = f.GetFieldAsString(fn);
                    break;
            }
            return r;
        }
        private Object GetFeatureFieldValueByIndex(Feature f, FieldDefn field, int fn)
        {
            
            Object r;
            switch (field.GetFieldType())
            {
                case FieldType.OFTInteger:
                    r = f.GetFieldAsInteger(fn);
                    break;
                case FieldType.OFTInteger64:
                    r = f.GetFieldAsInteger64(fn);
                    break;
                case FieldType.OFTString:
                    r = GetFieldValueString(f, fn, Encoding);
                    break;
                case FieldType.OFTReal:
                    r = f.GetFieldAsDouble(fn);
                    break;
                case FieldType.OFTWideString:
                    r = GetFieldValueString(f, fn, Encoding);
                    break;
                case FieldType.OFTBinary:
                    r = f.GetFieldAsString(fn);
                    break;
                case FieldType.OFTDate:
                    int year, month, day, h, m, fg;
                    float s;
                    f.GetFieldAsDateTime(fn, out year, out month, out day, out h, out m, out s, out fg);
                    r = new DateTime(year, month, day, h, m, fg);
                    break;
                case FieldType.OFTDateTime:
                    f.GetFieldAsDateTime(fn, out year, out month, out day, out h, out m, out s, out fg);
                    r = new DateTime(year, month, day, h, m, fg);
                    break;
                case FieldType.OFTTime:
                    f.GetFieldAsDateTime(fn, out year, out month, out day, out h, out m, out s, out fg);
                    r = new DateTime(year, month, day, h, m, fg);
                    break;
                default:
                    r = f.GetFieldAsString(fn);
                    break;
            }
            return r;
        }

        //public void ExportFeatures(Layer layer, List<Feature> features, MainModel mainModel)
        //{

        //    layer.StartTransaction();
        //    for (int i = 0; i < features.Count; i++)
        //    {
        //        Feature f = features[i];
        //        try
        //        {
        //            layer.CreateFeature(f);
        //            mainModel.Count++;
        //        }
        //        catch (Exception e)
        //        {
        //            //Log.InfoAsync(e.Message);
        //        }
        //        finally
        //        {
        //            f.Dispose();
        //        }
        //        if (i % 1000 == 0)
        //        {
        //            layer.CommitTransaction();
        //            layer.StartTransaction();
        //            layer.SyncToDisk();
        //        }
        //    }
        //    layer.CommitTransaction();
        //    layer.SyncToDisk();
        //}

        /// <summary>
        /// 创建CGCS2000大地坐标系的shp文件
        /// EPSG:4490
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <param name="geometryType">空间数据类型</param>
        /// <param name="layer">生成图层</param>
        /// <returns></returns>
        public DataSource CreateDataSource(String filePath, wkbGeometryType geometryType)
        {
            string strDriver = "ESRI Shapefile";
            Driver oDriver = Ogr.GetDriverByName(strDriver);

            DataSource ds = oDriver.CreateDataSource(filePath, null);

            OSGeo.OSR.SpatialReference sr = new OSGeo.OSR.SpatialReference(wkt);
            ds.CreateLayer("dmdz_" + geometryType.ToString(), sr, geometryType, null);

            return ds;
        }

    }
}
